package vg.modules.notepad.components;

import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;

import vg.modules.notepad.components.connectorComponent.Connector;
import vg.services.plugin_manager.event_and_request.event.UIEventCreateNewConnection;
import vg.services.plugin_manager.event_and_request.event.UIEventDeleteConnection;
import vg.services.plugin_manager.realization.PluginParameter;

/**
 * This class realizes connection statistic for the notepad.
 * All methods of this class by need called from EDT only!!!
 * @author tzolotuhin
 */
public class ConnectionStatistic extends JDialog {
	private final static long serialVersionUID = 1L;
	// Defines
	private final static int DEF_ROW_HEIGHT = 22;
	// Components
	private JTable table;
	private JScrollPane tableScroll;
	private final JPanel view;
	private final JButton /*addConnectionButton,*/ cancelButton;
	// Data
	private final List<UIEventCreateNewConnection> connections;
	private final PluginParameter param;
	private final Connector connector;
	/**
	 * Constructor.
	 */
	public ConnectionStatistic(final JFrame parent, final PluginParameter param) {
		super(parent, "Connection statistic", true);
		// create components
		this.param = param;
		this.connector = new Connector(param, parent);
		this.connections = new ArrayList<UIEventCreateNewConnection>();
		this.view = new JPanel(new GridBagLayout());
		this.cancelButton = new JButton("Cancel");
		// add listeners
		this.cancelButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				ConnectionStatistic.this.setVisible(false);
			}
		});
		// init ui
		this.setLayout(new GridLayout(1, 1));
		this.setSize(500,400);

		this.add(this.view);
		GridBagConstraints gbc = new GridBagConstraints(1,1, 1,1, 1,0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0,0);

		gbc = new GridBagConstraints(0,1, 1,1, 1,0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0,0);
		this.view.add(this.cancelButton, gbc);
		
		super.setLocationRelativeTo(parent);
	}
	/**
	 * This method opens the connection statistics panel.
	 */
	public synchronized void open() {
		this.setVisible(true);
	}
	/**
	 * This method adds new connection.
	 */
	public synchronized void addConnection(final UIEventCreateNewConnection connection) {
		this.connections.add(connection);
		rebuildTable();
	}
	/**
	 * This method removes existing connection from connection statistic panel.
	 */
	public synchronized void removeConnection(int connectionId) {
		for(UIEventCreateNewConnection buf : this.connections) {
			if(buf.getConnectionId() == connectionId) {
				this.connections.remove(buf);
				break;
			}
		}
		rebuildTable();
	}
	public synchronized void removeConnectionByFileId(int fileId) {
		for(UIEventCreateNewConnection buf : this.connections) {
			if(buf.getFileId() == fileId) {
				UIEventDeleteConnection event = new UIEventDeleteConnection(buf.getConnectionId());
				this.param.userInterface.addEvent(event);
				break;
			}
		}
		rebuildTable();
	} 
	public synchronized void updateUITheme() {
		SwingUtilities.updateComponentTreeUI(this);
	}
	public synchronized void removeAllConnections() {
		this.connections.clear();
		rebuildTable();
	}
	public synchronized List<UIEventCreateNewConnection> getAllConnections() {
		List<UIEventCreateNewConnection>buf = new ArrayList<UIEventCreateNewConnection>(this.connections);
		return(buf);
	} 
	///////////////////////////////////////////////////////////////////////////
	// PRIVATE METHODS
	///////////////////////////////////////////////////////////////////////////
	private void rebuildTable() {
		final Object[][] data = new Object[this.connections.size()][5];
		
		int i = 0;
		for(UIEventCreateNewConnection buf : this.connections) {
			data[i][0] = buf.getConnectionName();
			data[i][1] = buf.getGraphName();
			data[i][2] = buf.getAnchor();
			JButton edit = new JButton("Edit");
			edit.addActionListener(new EditListener(this.connector, buf));
			data[i][3] = edit;
			JButton delete = new JButton("Delete");
			delete.addActionListener(new DeleteListener(buf.getConnectionId(), param));
			data[i][4] = delete;
			i++;
		}
		
		if(ConnectionStatistic.this.table != null) {
			ConnectionStatistic.this.view.remove(ConnectionStatistic.this.tableScroll);
		}
		CSTableModel model = new CSTableModel(data);
		ConnectionStatistic.this.table = new JTable(model);
		ConnectionStatistic.this.table.setOpaque(true);
		ConnectionStatistic.this.table.setFillsViewportHeight(true);
		ConnectionStatistic.this.table.setRowHeight(DEF_ROW_HEIGHT);
		TableRowSorter<CSTableModel> sorter = new TableRowSorter<CSTableModel>(model);
		sorter.setSortable(0, true);
		sorter.setSortable(1, true);
		sorter.setSortable(2, true);
		sorter.setSortable(3, false);
		sorter.setSortable(4, false);
		ConnectionStatistic.this.table.setRowSorter(sorter);
		ConnectionStatistic.this.table.setShowGrid(true);
		ConnectionStatistic.this.table.getTableHeader().setReorderingAllowed(false);
		//---------------------------------------
		CSTableCellRenderer renderer = new CSTableCellRenderer();
		TableColumn col3 = ConnectionStatistic.this.table.getColumnModel().getColumn(3);
		TableColumn col4 = ConnectionStatistic.this.table.getColumnModel().getColumn(4);
		col3.setCellRenderer(renderer);
		col4.setCellRenderer(renderer);
		//---------------------------------------
		CSTableCellEditor editor = new CSTableCellEditor(model, renderer);
		col3.setCellEditor(editor);
		col4.setCellEditor(editor);
		//---------------------------------------
		ConnectionStatistic.this.tableScroll = new JScrollPane(ConnectionStatistic.this.table);
		GridBagConstraints gbc = new GridBagConstraints(0,0, 2,1, 1,1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0,0);
		ConnectionStatistic.this.view.add(ConnectionStatistic.this.tableScroll, gbc);
		ConnectionStatistic.this.view.updateUI();
	}
	///////////////////////////////////////////////////////////////////////////
	// PRIVATE CLASSES
	///////////////////////////////////////////////////////////////////////////
	/**
	 * This class realizes model for attribute table
	 * @author tzolotuhin
	 */
	private class CSTableModel extends AbstractTableModel {
		private static final long serialVersionUID = 1000L;
		//-------------------------------------------------------------------------
		private String[] columnNames = null;
	    private Object[][] table = null;
	    private final static int DEF_COUNT_COLUMN = 5;
		//-------------------------------------------------------------------------
	    public CSTableModel(final Object[][] data) {
	    	this.columnNames = new String[DEF_COUNT_COLUMN];
	    	this.table = data;
	       	this.columnNames = new String[]{"Name", "Graph name", "Anchor", "Edit", "Delete"};
	    }
	    public int getColumnCount() {   	
	    	return(DEF_COUNT_COLUMN);
	    }
	    public int getRowCount() {
	    	return(this.table.length);
	    }
	    public Object getValueAt(int rowIndex, int columnIndex) {
	    	return(this.table[rowIndex][columnIndex]);
	    }
	    public String getColumnName(int column) {
	    	return(this.columnNames[column]);
	    }
	    public Class<?> getColumnClass(int columnIndex) {
	    	if (getRowCount() > 0)
	    		return(this.table[0][columnIndex].getClass());
	    	return null;
	    }
	    public boolean isCellEditable(int rowIndex, int columnIndex) {
	    	if(columnIndex == 3 || columnIndex == 4) {
	    		return(true);
	    	}
	    	return(false);
	    }
	    /**
	     * This method sets value in table.
	     * @param value - value.
	     * @param row   - number of row.
	     * @param col   - number of column.
	     */
	    public void setValueAt(Object value, int row, int col) {
			this.table[row][col] = value;
	        fireTableCellUpdated(row, col);
	    }  
	}
	///////////////////////////////////////////////////////////////////////////
	// PRIVATE CLASSES
	///////////////////////////////////////////////////////////////////////////
	private class CSTableCellEditor extends AbstractCellEditor implements TableCellEditor {
		private static final long serialVersionUID = 1000L;
		//---------------------------------------------------------------------
		private Object lastObject;
		private CSTableCellRenderer renderer;
		public CSTableCellEditor(CSTableModel model, CSTableCellRenderer renderer) {
			this.renderer = renderer;
		}
		/**
		 *  This method is called when a cell value is edited by the user.
		 */
		public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int rowIndex, int colIndex) {
			this.lastObject = table.getValueAt(rowIndex, colIndex);
			return(this.renderer.getTableCellRendererComponent(table, value, isSelected, isSelected, rowIndex, colIndex));
		}
		/**
		 * This method is called when editing is completed.
		 * It must return the new value to be stored in the cell.
		 */
		public Object getCellEditorValue() {
			return(this.lastObject);
		}
	}
	private class CSTableCellRenderer implements TableCellRenderer { 
		private final DefaultTableCellRenderer DEFAULT_RENDERER = new DefaultTableCellRenderer();

		public Component getTableCellRendererComponent(final JTable table, final Object value, boolean isSelected, boolean hasFocus, int row, int column) { 
			switch(column) {
				case 3:case 4:
				{
					JButton obj = (JButton)table.getValueAt(row, column);
					return(obj);
				}
			}
			return(DEFAULT_RENDERER.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column));
		}
	}
	/**
	 * Listener for delete button.
	 * @author tzolotuhin
	 */
	private class DeleteListener implements ActionListener {
		private final int connectionId;
		private final PluginParameter param;
		public DeleteListener(int connectionId, final PluginParameter param) {
			this.param = param;
			this.connectionId = connectionId;
		}
		public void actionPerformed(ActionEvent e) {
			UIEventDeleteConnection event = new UIEventDeleteConnection(this.connectionId);
			this.param.userInterface.addEvent(event);
		}
	}
	/**
	 * Listener for edit button.
	 * @author tzolotuhin
	 */
	private class EditListener implements ActionListener {
		private final Connector connector;
		private final UIEventCreateNewConnection connection;
		public EditListener(final Connector connector, UIEventCreateNewConnection connection) {
			this.connector = connector;
			this.connection = connection;
		}
		public void actionPerformed(ActionEvent e) {
			this.connector.replace(this.connection.getConnectionId(), this.connection.getConnectionName(), this.connection.getFileId(), this.connection.getGraphId(), this.connection.getAnchor());
		}
	}
}
